package com.pesking.core.sqlparser;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

import com.pesking.core.exception.SystemException;
import com.pesking.core.parameter.Parameter;
import com.pesking.core.util.DateUtils;
import com.pesking.core.util.LogUtils;

public abstract class ListSqlResult<T> extends SqlResult<List<T>> {
	
	private Map<String,Method> methodMap = new HashMap<>();
	
	protected T row = null;
	private Class<?> recordType = null;
	
	@SuppressWarnings("unchecked")
	protected T newRecord() {
		if (recordType == null) {
			recordType = getRecordType();
		}
		try {
			return (T) recordType.getDeclaredConstructor().newInstance();
		} catch (InstantiationException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		} catch (IllegalAccessException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		} catch (IllegalArgumentException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		} catch (InvocationTargetException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		} catch (NoSuchMethodException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		} catch (SecurityException e) {
			LogUtils.error(ExceptionUtils.getStackTrace(e));
			throw SystemException.throwSystemException(e);
		}
	}
	
	protected Class<?> getRecordType(){
		if (recordType == null) {
			Class<?> clazz = this.getClass();
			Type type=clazz.getGenericSuperclass();
			ParameterizedType p=(ParameterizedType)type;
			recordType = (Class<?>) p.getActualTypeArguments()[0];
		}
		return recordType;
	}

	@Override
	protected void beforeRecord() throws SQLException {
		this.row  = newRecord();
	}

	@Override
	protected void afterRecord() throws SQLException {
		this.result.add(row);
	}

	public ListSqlResult(Parameter requestParameter) {
		super(requestParameter);
	}

	@Override
	protected List<T> newResult() {
		return new ArrayList<>();
	}

	@Override
	protected void beforeResult(ResultSet rs)  throws SQLException{
		Method []arr = this.result.getClass().getMethods();
		if (arr != null){
			int len = arr.length;
			for(int i = 0 ; i < len ; i++){
				Method m = arr[i];
				String name = m.getName();
				if (name.startsWith("set") && m.getParameterTypes().length == 1){
					methodMap.put(name.toLowerCase(), m);
				}
			}
		}
	}

	@Override
	protected void dealColumn(ColumnInfo ci, String columnName, String columnValue) {
		Object objectValue = null;
		if (!StringUtils.isBlank(columnValue)) {
			switch (ci.columnType){
			case java.sql.Types.VARCHAR:
				objectValue = columnValue;
				break;
			case java.sql.Types.CHAR:
				objectValue = columnValue;
				break;
			case java.sql.Types.DATE:
				objectValue = DateUtils.string2Date(columnValue);
				break;
			case java.sql.Types.TIME:
				objectValue = DateUtils.string2Date(columnValue);
				break;
			case java.sql.Types.FLOAT:
				objectValue = Float.valueOf(columnValue);
				break;
			case java.sql.Types.REAL:
				objectValue = Float.valueOf(columnValue);
				break;				
			case java.sql.Types.DOUBLE:
				objectValue = Double.valueOf(columnValue);
				break;
			case java.sql.Types.INTEGER:
				objectValue = Integer.valueOf(columnValue);
				break;
			case java.sql.Types.NUMERIC:
				objectValue = new BigDecimal(columnValue);
				break;
			case java.sql.Types.DECIMAL:
				objectValue = new BigDecimal(columnValue);
				break;
			case java.sql.Types.SMALLINT:
				objectValue = Short.valueOf(columnValue);
				break;
			case java.sql.Types.TIMESTAMP:
				objectValue = DateUtils.string2Timestamp(columnValue);
				break;
			case java.sql.Types.BIT:
				objectValue = Byte.valueOf(columnValue);
				break;
			case java.sql.Types.TINYINT:
				objectValue = Byte.valueOf(columnValue);
				break;
			case java.sql.Types.BIGINT:
				objectValue = Long.valueOf(columnValue);
				break;
			case java.sql.Types.NCHAR:
				objectValue = columnValue;
				break;
			case java.sql.Types.LONGVARCHAR:
				objectValue = columnValue;
				break;
			default:
				objectValue = columnValue;
				LogUtils.warn("系统并未对类型[" + ci.columnType + "]作出处理,请与开发人员进行联系.");
			}
		}		
		
		setColumnValue(ci.columnName,objectValue);
	}

	protected void setColumnValue(String columnName,Object columnValue) {
		String key = "set" + columnName.toLowerCase();
		if (methodMap.containsKey(key)){
			//包含此方法
			Method m = (Method)methodMap.get(key);			
			if (columnValue != null){
				try {
					m.invoke(row, new Object[]{columnValue});
				} catch (IllegalArgumentException e) {
					LogUtils.error(ExceptionUtils.getStackTrace(e));
					throw SystemException.throwSystemException(e);
				} catch (IllegalAccessException e) {
					LogUtils.error(ExceptionUtils.getStackTrace(e));
					throw SystemException.throwSystemException(e);
				} catch (InvocationTargetException e) {
					LogUtils.error(ExceptionUtils.getStackTrace(e));
					throw SystemException.throwSystemException(e);
				}
			}
		}
	}
}
